package asteroid

import (
	"fmt"
	"sync"

	"gddgame.cc/galaxy/utils/def"
)

type Region struct {
	def.Logger

	layer *Layer
	id    string
	adapt Adapt

	rooms sync.Map

	*nodeManager
}

func newRegion(layer *Layer, node string, id string) *Region {
	r := &Region{
		layer:  layer,
		Logger: layer.LoggerWrap("Region", id),
		id:     id,
		adapt:  nil,

		rooms: sync.Map{},
	}
	r.nodeManager = newNodeManager(layer, r.Logger, node, true)
	if err := r.setAdapt(layer.regionAdapt); err != nil {
		r.Fatalf("Region:%s setAdapt", id, err)
	} else {
		//r.Debugf("Region:%s setAdapt", id)
	}
	return r
}
func (r *Region) ID() string {
	return r.id
}

func (r *Region) setAdapt(a Adapt) error {
	if a == nil {
		return nil
	}
	key := r.ID()
	identity := "region:" + r.node + key
	if r.adapt != nil {
		if err := r.adapt.UnRegister(identity); err != nil {
			return err
		}
		if err := r.adapt.UnMonitor(identity); err != nil {
			return err
		}
	}
	r.adapt = a

	// 先监听，再注册
	if err := r.adapt.Monitor(identity, key, func(node string) {
		r.join(node)
	}, func(node string) {
		r.leave(node)
		r.rooms.Range(func(key, value interface{}) bool {
			value.(*Room).leave(node)
			return true
		})
	}, func() {
		r.finish()
	}); err != nil {
		return err
	}
	return r.adapt.Register(identity, key, r.layer.node)
}

func (r *Region) dispatch(msg *Msg) bool {
	i, ok := r.rooms.Load(msg.Key)
	if !ok {
		return false
	}
	//startTime := time.Now()
	room := i.(*Room)
	msg.responseAction = room
	room.dispatch(msg)
	//msg.message.Logger = r.LoggerWrap(r.layer.trackKey, msg.Origin)
	//endTime := time.Now().Sub(startTime)
	//if endTime > 10*time.Millisecond {
	//	fmt.Println("region send", endTime)
	//	debug.PrintStack()
	//}
	return true
}

func (r *Region) send(msg *Msg) {
	if msg.to == nil {
		msg.to = r.nodes
	}

	//fmt.Println("send", msg.Key, msg.Event, msg.Origin, msg.Target, msg.to)
	msg.From = r.node
	msg.layer = r.layer
	if err := msg.Send(RegionMessage, r.dispatch); err != nil {
		r.Error(err)
	}
}

func (r *Region) HasRoom(id string) bool {
	_, ok := r.rooms.Load(id)
	return ok
}

func (r *Region) OpenRoom(id string) (*Room, bool) {
	i, ok := r.rooms.Load(id)
	if ok {
		return i.(*Room), true
	}
	room := newRoom(r, id)
	r.rooms.Store(id, room)
	return room, false
}

func (r *Region) CloseRoom(id string) error {
	i, ok := r.rooms.Load(id)
	if !ok {
		return fmt.Errorf("room %s is not exist", id)
	}
	r.rooms.Delete(id)
	i.(*Room).close()
	return nil
}

func (r *Region) Close() error {
	//r.Debugf("[ Region ] Close %s Room", r.id)
	r.rooms.Range(func(key, value interface{}) bool {
		value.(*Room).close()
		return true
	})
	r.rooms = sync.Map{}
	return nil
}

func (r *Region) Count() int {
	return len(r.nodes)
}

// 未开通房间的pipe会导致对方发送消息丢失
func (r *Region) All(roomId string) *Pipe {
	return r.layer.Pipe().setSend(func(pipe *Pipe) {
		msg := newMsg(RequestRoomMessageType, r.id, roomId)
		msg.Pipe(pipe, nil)
		r.send(msg)
	}).Nodes(r.allNodes)
}
func (r *Region) Broadcast(roomId string) *Pipe {
	return r.layer.Pipe().setSend(func(pipe *Pipe) {
		msg := newMsg(BroadcastRoomMessageType, r.id, roomId)
		msg.Pipe(pipe, nil)
		r.send(msg)
	}).Nodes(r.allNodes)
}
